package com.fa4j.system.login.service.impl;


import com.fa4j.common.base.exception.CommonServerError;
import com.fa4j.common.base.model.CommonState;
import com.fa4j.common.base.util.ValidUtil;
import com.fa4j.common.web.model.AppType;
import com.fa4j.common.web.model.ClientInfo;
import com.fa4j.system.account.model.Account;
import com.fa4j.system.account.model.AccountBind;
import com.fa4j.system.account.model.AccountBindCmd;
import com.fa4j.system.account.model.AccountPasswordType;
import com.fa4j.system.account.repository.AccountRepository;
import com.fa4j.system.account.service.AccountBindService;
import com.fa4j.system.account.service.AccountPasswordService;
import com.fa4j.system.login.model.*;
import com.fa4j.system.login.service.CaptchaService;
import com.fa4j.system.login.service.LoginService;
import com.fa4j.system.login.service.LoginSessionService;
import jakarta.validation.Validator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.Optional;

/**
 * 登录服务
 */
@Slf4j
@Service
@Validated
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {


    private final AccountRepository accountRepository;

    private final LoginPropsServiceImpl loginPropsServiceImpl;
    private final AccountPasswordService accountPasswordService;
    private final CaptchaService captchaService;
    private final AccountBindService accountBindService;
    private final LoginSessionService loginSessionService;

    private final Validator validator;



    /**
     * 登录
     *
     * @param appType 应用类型
     * @param cmd     登录参数
     * @return 登录会话
     */
    @Override
    public LoginSession doLogin(AppType appType, LoginCmd cmd, ClientInfo clientInfo) {
        LoginAppProps prop = loginPropsServiceImpl.getAppTypeItemProp(appType);
        if (!prop.getLoginTypes().contains(cmd.getLoginType())) {
            throw CommonServerError.NOT_IMPLEMENTED.build(cmd.getLoginType().getName());
        }
        Account account = null;
        switch (cmd.getLoginType()) {
            case PWD -> {
                ValidUtil.valid(validator, cmd, LoginCmd.PwdLogin.class);
                account = doPwdLogin(appType, cmd.getLoginName(), cmd.getLoginPwd());
            }
            case SMS -> {
                ValidUtil.valid(validator, cmd, LoginCmd.SmsLogin.class);
                account = doSmsLogin(appType, cmd.getLoginName(), cmd.getAuthCode());
            }
            case DINGTALK, WECHAT -> {
                ValidUtil.valid(validator, cmd, LoginCmd.CodeLogin.class);
                account = doThirdLogin(appType, prop, cmd.getLoginType(), cmd.getAuthCode());
            }
        }
        if (account == null) {
            throw LoginError.ACCOUNT_NOT_REGISTER.build();
        }
        if (account.getState() == CommonState.DISABLE) {
            throw LoginError.ACCOUNT_DISABLE.build();
        }
        return loginSessionService.createSession(account, clientInfo);
    }

    private Account doThirdLogin(AppType appType, LoginAppProps prop, LoginType loginType, String authCode) {
        AccountBindCmd bindCmd = new AccountBindCmd();
        bindCmd.setAppType(appType);
        bindCmd.setLoginType(loginType);
        bindCmd.setThirdCode(authCode);
        bindCmd.setRegisterEnable(prop.isRegisterEnabled());
        // 绑定三方账号，如果已经绑定，则返回绑定的信息
        AccountBind bind = accountBindService.bind(bindCmd);

        return Optional.ofNullable(bind)
                .flatMap(b -> accountRepository.findById(b.getAccountId()))
                .orElseThrow(LoginError.ACCOUNT_NOT_REGISTER::build);
    }


    private Account doPwdLogin(AppType appType, String loginName, String loginPwd) {
        return accountRepository.findByUsername(appType, loginName)
                .filter(a -> accountPasswordService.validPwd(a.getId(), AccountPasswordType.LOGIN, loginPwd))
                .orElseThrow(LoginError.ERROR_USERNAME_OR_PASSWORD::build);
    }

    private Account doSmsLogin(AppType appType, String phone, String smsCode) {
        //验证短信
        captchaService.validCaptcha(CaptchaType.SMS_LOGIN, phone, smsCode);
        return accountRepository.findByPhone(appType, phone)
                .orElseThrow(LoginError.ACCOUNT_NOT_REGISTER::build);
    }


}
